/*
 * Copyright (c) 2019 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bas_client.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(basClient, LOG_LEVEL_DBG);

/**
 * @brief Process battery level value notification
 *
 * Internal function to process report notification and pass it further.
 *
 * @param conn   Connection handler.
 * @param params Notification parameters structure - the pointer
 *               to the structure provided to subscribe function.
 * @param data   Pointer to the data buffer.
 * @param length The size of the received data.
 *
 * @retval BT_GATT_ITER_STOP     Stop notification
 * @retval BT_GATT_ITER_CONTINUE Continue notification
 */
static uint8_t notify_process(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
							  const void *data, uint16_t length)
{
	struct bas_client *bas;
	uint8_t battery_level;
	const uint8_t *bdata = data;

	bas = CONTAINER_OF(params, struct bas_client, notify_params);
	if (!data || !length)
	{
		LOG_INF("Notifications disabled.");
		return BT_GATT_ITER_STOP;
	}
	if (length != 1)
	{
		LOG_ERR("Unexpected notification value size.");
		return BT_GATT_ITER_STOP;
	}

	battery_level = bdata[0];
	if (battery_level > BT_BAS_VAL_MAX)
	{
		LOG_ERR("Unexpected notification value.");
		return BT_GATT_ITER_STOP;
	}
	bas->battery_level = battery_level;

	char addr[BT_ADDR_LE_STR_LEN];
	bt_addr_le_to_str(bt_conn_get_dst(bas_conn(bas)), addr, sizeof(addr));
	if (battery_level == BT_BAS_VAL_INVALID)
	{
		LOG_WAN("[%s] Battery notification aborted\n", addr);
	}
	else
	{
		LOG_INF("[%s] Battery notification: %" PRIu8 "%%\n", addr, battery_level);
	}

	return BT_GATT_ITER_CONTINUE;
}

/**
 * @brief Process battery level value read
 *
 * Internal function to process report read and pass it further.
 *
 * @param conn   Connection handler.
 * @param err    Read ATT error code.
 * @param params Notification parameters structure - the pointer
 *               to the structure provided to read function.
 * @param data   Pointer to the data buffer.
 * @param length The size of the received data.
 *
 * @retval BT_GATT_ITER_STOP     Stop notification
 * @retval BT_GATT_ITER_CONTINUE Continue notification
 */
static uint8_t read_process(struct bt_conn *conn, uint8_t err, struct bt_gatt_read_params *params,
							const void *data, uint16_t length)
{
	struct bas_client *bas;
	uint8_t battery_level = BT_BAS_VAL_INVALID;
	const uint8_t *bdata = data;

	bas = CONTAINER_OF(params, struct bas_client, read_params);

	if (err)
	{
		LOG_ERR("Read value error: %d", err);
	}
	else if (!data || length != 1)
	{
		LOG_ERR("Read value error: %d", -EMSGSIZE);
	}
	else
	{
		battery_level = bdata[0];
		if (battery_level > BT_BAS_VAL_MAX)
		{
			LOG_ERR("Unexpected read value.");
		}
		else
		{
			bas->battery_level = battery_level;
			char addr[BT_ADDR_LE_STR_LEN];
			bt_addr_le_to_str(bt_conn_get_dst(bas_conn(bas)), addr, sizeof(addr));		
			LOG_INF("[%s] Battery read: %" PRIu8 "%%\n", addr, battery_level);
		}
	}

	return BT_GATT_ITER_STOP;
}



/**
 * @brief Reinitialize the BAS Client.
 *
 * @param bas BAS Client object.
 */
static void bas_reinit(struct bas_client *bas)
{
	bas->ccc_handle = 0;
	bas->val_handle = 0;
	bas->battery_level = BT_BAS_VAL_INVALID;
	bas->conn = NULL;
	bas->notify = false;
}

void bas_client_init(struct bas_client *bas)
{
	memset(bas, 0, sizeof(*bas));
	bas->battery_level = BT_BAS_VAL_INVALID;
}


int bas_handles_assign(struct bt_gatt_dm *dm, struct bas_client *bas)
{
	const struct bt_gatt_dm_attr *gatt_service_attr = bt_gatt_dm_service_get(dm);	// attribute
	const struct bt_gatt_service_val *gatt_service = bt_gatt_dm_attr_service_val(gatt_service_attr);	// service declaration attribute value
	const struct bt_gatt_dm_attr *gatt_chrc;
	const struct bt_gatt_dm_attr *gatt_desc;
	const struct bt_gatt_chrc *chrc_val;

	if (bt_uuid_cmp(gatt_service->uuid, BT_UUID_BAS))	// compare the service uudi
	{
		return -ENOTSUP;
	}
	LOG_DBG("Getting handles from battery service.");

	/* When workqueue is used its instance cannont be cleared. */
	bas_reinit(bas);

	/* Battery level characteristic */
	// gatt_chrc:包含2a19的characteristic declaration attribute
	// 通过uuid找到对应的characteristic
	gatt_chrc = bt_gatt_dm_char_by_uuid(dm, BT_UUID_BAS_BATTERY_LEVEL); // 2a19
	if (!gatt_chrc)
	{
		LOG_ERR("No battery level characteristic found.");
		return -EINVAL;
	}
	// 通过characteristic declaration 找到它的attribute value部分
	chrc_val = bt_gatt_dm_attr_chrc_val(gatt_chrc);
	__ASSERT_NO_MSG(chrc_val); /* This is internal function and it has to
								* be called with characteristic attribute
								*/
	bas->properties = chrc_val->properties;
	// gatt_desc: characteristic value attribute
	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_BAS_BATTERY_LEVEL);
	if (!gatt_desc)
	{
		LOG_ERR("No battery level characteristic value found.");
		return -EINVAL;
	}
	bas->val_handle = gatt_desc->handle;

	// gatt_desc: characteristic descriptor attribute
	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_GATT_CCC);
	if (!gatt_desc)
	{
		LOG_INF("No battery CCC descriptor found. Battery service do not supported "
				"notification.");
	}
	else
	{
		bas->notify = true;
		bas->ccc_handle = gatt_desc->handle;
	}

	/* Finally - save connection object */
	bas->conn = bt_gatt_dm_conn_get(dm);
	return 0;
}

int bas_subscribe_battery_level(struct bas_client *bas)
{
	int err;

	if (!bas)
	{
		return -EINVAL;
	}
	if (!bas->conn)
	{
		return -EINVAL;
	}
	if (!(bas->properties & BT_GATT_CHRC_NOTIFY))
	{
		return -ENOTSUP;
	}

	bas->notify_params.notify = notify_process;
	bas->notify_params.value = BT_GATT_CCC_NOTIFY;
	bas->notify_params.value_handle = bas->val_handle;
	bas->notify_params.ccc_handle = bas->ccc_handle;
	atomic_set_bit(bas->notify_params.flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE);

	LOG_DBG("Subscribe: val: %u, ccc: %u", bas->notify_params.value_handle,	bas->notify_params.ccc_handle);
	err = bt_gatt_subscribe(bas->conn, &bas->notify_params);
	if (err)
	{
		LOG_ERR("Report notification subscribe error: %d.", err);
		return err;
	}
	LOG_DBG("Report subscribed.");
	return err;
}

int bas_unsubscribe_battery_level(struct bas_client *bas)
{
	int err;

	if (!bas)
	{
		return -EINVAL;
	}

	err = bt_gatt_unsubscribe(bas->conn, &bas->notify_params);
	return err;
}

struct bt_conn *bas_conn(const struct bas_client *bas)
{
	return bas->conn;
}

int bas_read_battery_level(struct bas_client *bas)
{
	int err;

	if (!bas)
	{
		return -EINVAL;
	}
	if (!bas->conn)
	{
		return -EINVAL;
	}

	bas->read_params.func = read_process;
	bas->read_params.handle_count = 1;
	bas->read_params.single.handle = bas->val_handle;
	bas->read_params.single.offset = 0;

	err = bt_gatt_read(bas->conn, &bas->read_params);
	if (err)
	{
		return err;
	}
	return 0;
}